            

%{


#include <iostream>
#include "tp.h"

#include <stdio.h>
#include <string.h>
extern "C"
{
        int yyparse(void);
        int yylex(void);  
        int yywrap()
        {
                return 1;
        }
        extern FILE * yyin;
        void analizo_grafo(nodeType *);
        int yyval =  0;
        
 
}


/* prototypes */

nodeType *buildNode(char c, int tipo);
nodeType *joinThreeNodes(nodeType * h1, nodeType * h2, nodeType * h3, int tipo);
nodeType *joinTwoNodes(nodeType * h1, nodeType * h2, int tipo);
nodeType *joinSingleNode(nodeType * h1, int tipo);

void yyerror(const char *str)
{
        if("syntax error" != str)
        {
            fprintf(stderr,"error: %s\n",str);
        }
        
}
 


%}

%nonassoc '_' '^'  

%union {
    char iValue;                 /* integer value */
    nodeType *nPtr;             /* node pointer */
};

%type <nPtr> analizar e c f g t
%token <iValue> CARACTER
%token PLACEHOLDER
%% 
inicio: |
        inicio analizar
        |
        error PLACEHOLDER
        {
            yyerror("Formula invalida.");
        }
        ;
analizar: 
        e PLACEHOLDER
        {
            $$=joinSingleNode($1, RAIZ);
            analizo_grafo($$);
        }
        ;
        
        
        
        
e:
        e '/' f
        {
                $$=joinTwoNodes($1, $3, DIVISION);
                #ifdef VERBOSE
                std::cout << "reconozco e/e: "  << std::endl;
                #endif
        }
        |
        f
        {
                $$=joinSingleNode($1, CONVERSION);
                #ifdef VERBOSE
                std::cout << "reconozco f: "  << std::endl;
                #endif
        }
        ;
 
 
f:
        f g
        {
                $$=joinTwoNodes($1, $2, CONCATENACION);
                #ifdef VERBOSE
                std::cout << "reconozco f g: "  << std::endl;
                #endif
        }
        |
        g
        {
                $$=joinSingleNode($1, CONVERSION);
                #ifdef VERBOSE
                std::cout << "reconozco g: "  << std::endl;
                #endif
        } 
        ;

g:
        t '^' t 
        {
                $$=joinTwoNodes($1, $3, SUPERINDICE);
                #ifdef VERBOSE
                std::cout << "reconozco t^t: "  << std::endl;
                #endif
        }
        |
        t '_' t
        {
                $$=joinTwoNodes($1, $3, SUBINDICE);
                #ifdef VERBOSE
                std::cout << "reconozco t_t: "  << std::endl;
                #endif
        }
        |
        t
        {
                $$=joinSingleNode($1, CONVERSION);
                
                #ifdef VERBOSE
                std::cout << "reconozco t: "  << std::endl;
                #endif
        }
        ;
        
        
c:        
        CARACTER
        {
                $$=buildNode($1, TERMINAL);
                $$->h2 = 0.0f;
                char temp = $1;
                #ifdef VERBOSE
                std::cout << "reconozco caracter: " << temp << std::endl;
                #endif
        }
        ;
        
        
t:
        '(' e ')' 
        {
                $$=joinSingleNode($2, PARENTESIS);
        }
        |
        '{' e '}'
        {
                $$=joinSingleNode($2, CONVERSION);
        }
        |
        t '^' t '_' t 
        {
                $$=joinThreeNodes($1, $3, $5, SUPERSUB);
        }
        |
        t '_' t '^' t 
        {
                $$=joinThreeNodes($1, $3, $5, SUBSUPER);
        }
        |
        c 
        {
                $$=joinSingleNode($1, C2CHAR);
        }
        ;

%%


nodeType *buildNode(char c, int tipo) {
    nodeType *p;
    
    p = new nodeType();
    p->caracter = c;
    p->tipo = tipo;
    p->hijo1 = NULL;
    p->hijo2 = NULL;
    p->hijo3 = NULL;
    return p;
}

nodeType * joinSingleNode(nodeType * h1, int tipo) {
    nodeType *p;
    p = buildNode('R', tipo);
    p->hijo1 = (int *)h1;
    return p;
}

nodeType *joinTwoNodes(nodeType * h1, nodeType * h2, int tipo) {
    nodeType *p = joinSingleNode(h1, tipo);
    p->hijo2 = (int *)h2;
    return p;
}

nodeType *joinThreeNodes(nodeType * h1, nodeType * h2, nodeType * h3, int tipo) {
    nodeType *p = joinTwoNodes(h1, h2,  tipo);
    p->hijo3 = (int *)h3;
    return p;
}


void yyerror(char *s) {
    fprintf(stdout, "%s\n", s);
}

#include <fstream>
int main(int argc, char * argv[]) {
    if(argc < 2)
    {
        std::cout << "Debe ingresar como parametro una formula entre comillas. Por ej.: \"formula\"" << std::endl;
        return 0;
    }
    std::ofstream temp("temp.tlen");
    temp << argv[1] << std::endl;
    temp.close();
    yyin = fopen("temp.tlen", "r");
    yyparse();
    if(std::remove("temp.tlen"))
    {
        std::cout << "No se pudo eliminar el archivo temporal temp.tlen, eliminarlo manualmente." << std::endl;
    }
    return 0;
}
float max_float(float x, float y)
{
    if(x > y)
    {
        return x;
    }
    else
    {
        return y;
    }
}

const float cuanto_subo = 0.45f;
const float cuanto_bajo = cuanto_subo/ 2.0f;
const float cuanto_subo_division = 0.28f;
infoSintetizados propagar_escala(nodeType * n)
{
#ifdef VERBOSE
    std::cout << "propagar_escala, tipo: " << decime_tipo(n->tipo) << std::endl;
#endif
    bool ya_entre = false;
    infoSintetizados result;
    if(n->tipo == TERMINAL && !ya_entre)
    {
        ya_entre = true;
        result.h2 = 0;
        result.h1 = n->escala;
        result.ancho = 0.6f * n->escala;
    }
    if(n->tipo == CONCATENACION && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        hijo2->escala = n->escala;
        infoSintetizados temp2 = propagar_escala(hijo2);
        result.ancho = temp.ancho + temp2.ancho;
        result.h1 = max_float(temp.h1, temp2.h1);
        result.h2 = max_float(temp.h2, temp2.h2);
    }
    if(n->tipo == SUBINDICE && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        hijo2->escala = n->escala * 0.7f;
        infoSintetizados temp2 = propagar_escala(hijo2);
        result.ancho = temp.ancho + temp2.ancho;
        result.h1 = max_float(temp.h1, temp2.h1 - cuanto_bajo * n->escala)  ;
        result.h2 = max_float(temp.h2, temp2.h2 + cuanto_bajo * n->escala);
        
    }
    if(n->tipo == SUPERINDICE && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        hijo2->escala = n->escala * 0.7f;
        infoSintetizados temp2 = propagar_escala(hijo2);
        result.ancho = temp.ancho + temp2.ancho;
        result.h1 = max_float(temp.h1, temp2.h1 + cuanto_subo * n->escala);
        result.h2 =  max_float(temp.h2, temp2.h2 - cuanto_subo * n->escala)  ;
    }
    if(n->tipo == DIVISION && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        hijo2->escala = n->escala;
        infoSintetizados temp2 = propagar_escala(hijo2);
        result.ancho = max_float(temp.ancho, temp2.ancho);
        result.h1 = temp.h1 - cuanto_subo_division * n->escala + 0.2 *n->escala;
        result.h2 = temp2.h1 - cuanto_subo_division * n->escala - 0.1 *n->escala+ temp2.h2;
        
        
    }
    if(n->tipo == PARENTESIS && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        
        result.ancho = temp.ancho + 1.2f * n->escala;
        result.h1 = temp.h1;
        result.h2 = temp.h2;
    }
    if(n->tipo == SUBSUPER && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        nodeType * hijo3 = ((nodeType *) n->hijo3);
        
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        
        
        hijo2->escala = n->escala * 0.7f;
        infoSintetizados temp2 = propagar_escala(hijo2);
        
        
        
        hijo3->escala = n->escala * 0.7f;
        infoSintetizados temp3 = propagar_escala(hijo3);
        
        result.ancho = temp.ancho + max_float(temp2.ancho, temp3.ancho);
        result.h1 = max_float( temp.h1, temp3.h1 + (cuanto_subo * n->escala));
        result.h2 = max_float( temp.h2, temp2.h2 + (cuanto_bajo * n->escala));
        
        
    }
    if(n->tipo == SUPERSUB && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        nodeType * hijo3 = ((nodeType *) n->hijo3);
        
        hijo1->escala = n->escala;
        infoSintetizados temp = propagar_escala(hijo1);
        
        
        hijo2->escala = n->escala * 0.7f;
        infoSintetizados temp2 = propagar_escala(hijo2);
        
        
        
        hijo3->escala = n->escala * 0.7f;
        infoSintetizados temp3 = propagar_escala(hijo3);
        
        result.ancho = temp.ancho + max_float(temp2.ancho, temp3.ancho);
        result.h1 = max_float( temp.h1, temp2.h1 + (cuanto_subo * n->escala));
        result.h2 = max_float( temp.h2, temp3.h2 + (cuanto_bajo * n->escala));
    }
    if(( n->tipo == C2CHAR ||n->tipo == RAIZ || n->tipo == CONVERSION) && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        
        hijo1->escala = n->escala;
        result = propagar_escala(hijo1);
        
    }
    if(!ya_entre)
    {
        #ifdef VERBOSE
        std::cout << "PROBLEMON: el tipo del nodo es cualquier cosa!!" << std::endl;
        #endif
    }
    n->h1 = result.h1;
    n->h2 = result.h2;
    n->ancho = result.ancho;
    return result;
}
std::ofstream mi_output;
void propagar_heredados(nodeType * n)
{
#ifdef VERBOSE
    std::cout << "propagar_heredados, tipo: " << decime_tipo(n->tipo) << " ancho: " << n->ancho << std::endl;
    std::cout << "caracter: " <<  n->caracter<<", h1:  " << n->h1 << std::endl;
    std::cout << "caracter: " <<  n->caracter<<", h2:  " << n->h2 << std::endl;
    std::cout << "caracter: " <<  n->caracter<<", escala:  " << n->escala << std::endl;
    std::cout << "caracter: " <<  n->caracter<<", x:  " << n->x << std::endl;
    std::cout << "caracter: " <<  n->caracter<<", y:  " << n->y << std::endl;
#endif
    bool ya_entre = false;
    if(n->tipo == TERMINAL && !ya_entre)
    {
#ifdef VERBOSE
        std::cout << "caracter: " <<  n->caracter<<", ancho:  " << n->ancho << std::endl;
        std::cout << "caracter: " <<  n->caracter<<", y:  " << n->x << std::endl;
#endif
        ya_entre = true;
        mi_output.precision(2);
        mi_output << "gsave ";
        mi_output << n->x << " " << n->y << " moveto ";
        mi_output << n->escala << " " << n->escala;
        mi_output << " scale (" << n->caracter << ") show grestore"   << std::endl;
    }
    if(n->tipo == CONCATENACION && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
        hijo2->x = n->x + hijo1->ancho;
        hijo2->y = n->y;
        propagar_heredados(hijo2);
    }
    if(n->tipo == SUBINDICE && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
        
        hijo2->x = n->x + hijo1->ancho;
        hijo2->y = n->y - cuanto_bajo * n->escala; // Ver cuanto hace falta bajarlo
        propagar_heredados(hijo2);
        
        
    }
    if(n->tipo == SUPERINDICE && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
        
        hijo2->x = n->x + hijo1->ancho;
        hijo2->y = n->y + cuanto_subo * n->escala; // Ver cuanto hace falta subirlo
        propagar_heredados(hijo2);
        
    }
    if(n->tipo == DIVISION && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        
        nodeType * hijo_ancho = ((nodeType *) n->hijo1);
        nodeType * hijo_flaco = ((nodeType *) n->hijo2);
        if(hijo1->ancho > hijo2->ancho)
        {
            hijo_ancho = hijo1;
            hijo_flaco = hijo2;
        }
        else
        {
            hijo_ancho = hijo2;
            hijo_flaco = hijo1;
        }
        hijo_flaco->x = n->x + (hijo_ancho->ancho - hijo_flaco->ancho) / 2.0f;
        hijo_ancho->x = n->x;
        hijo1->y = n->y + hijo1->h2 + cuanto_subo_division * n->escala +  0.2 *n->escala;
        propagar_heredados(hijo1);
        
        hijo2->y = n->y - ( hijo2->h1) + cuanto_subo_division * n->escala+ 0.1 *n->escala; // Ver cuanto hace falta bajarlo
        propagar_heredados(hijo2);
        
        
        mi_output.precision(2);
        mi_output << "gsave ";
        mi_output << n->x << " " << (n->y + cuanto_subo_division) << " moveto ";
        mi_output << n->ancho << " " << 0.0f;
        mi_output << " rlineto stroke grestore"   << std::endl;
    }
    if(n->tipo == PARENTESIS && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        hijo1->x = n->x + 0.6f* n->escala;
        hijo1->y = n->y ;
        propagar_heredados(hijo1);
        mi_output.precision(2);
        mi_output << "gsave ";
        mi_output << n->x  << " " << n->y - n->h2 * 0.5f * n->escala << " moveto ";
        mi_output << n->escala <<" " << (n->h1 + n->h2) *1.5f* n->escala << " scale (\\() show grestore" << std::endl;
       
       
        mi_output << "gsave ";
        mi_output << n->x -0.6f * n->escala + n->ancho << " " << n->y - n->h2 * 0.5f * n->escala<< " moveto ";
        mi_output << n->escala << " " << (n->h1 + n->h2) *1.5f* n->escala << " scale (\\)) show grestore" << std::endl;
       
        // Los parametros de scale estan hechos a mano para el parentesis, testear bien!
        // Lo mismo para el moveto
    }
    if(n->tipo == SUBSUPER && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        nodeType * hijo3 = ((nodeType *) n->hijo3);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
        
        hijo2->x = n->x + hijo1->ancho;
        hijo2->y = n->y - cuanto_bajo * n->escala; // Ver cuanto hace falta bajarlo
        propagar_heredados(hijo2);
        
        
        hijo3->x = n->x + hijo1->ancho;
        hijo3->y = n->y + cuanto_subo * n->escala; // Ver cuanto hace falta subirlo
        propagar_heredados(hijo3);
    }
    if(n->tipo == SUPERSUB && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        nodeType * hijo2 = ((nodeType *) n->hijo2);
        nodeType * hijo3 = ((nodeType *) n->hijo3);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
        
        
        hijo2->x = n->x + hijo1->ancho;
        hijo2->y = n->y + cuanto_subo * n->escala; // Ver cuanto hace falta subirlo
        propagar_heredados(hijo2);
        
        hijo3->x = n->x + hijo1->ancho;
        hijo3->y = n->y - cuanto_bajo * n->escala; // Ver cuanto hace falta bajarlo
        propagar_heredados(hijo3);
        
        
    }
    if((n->tipo == C2CHAR || n->tipo == RAIZ || n->tipo == CONVERSION) && !ya_entre)
    {
        ya_entre = true;
        nodeType * hijo1 = ((nodeType *) n->hijo1);
        hijo1->x = n->x;
        hijo1->y = n->y;
        propagar_heredados(hijo1);
    }

    if(n->tipo == C2CHAR && !ya_entre)
    {
        ya_entre = true;
    }
    if(!ya_entre)
    {
        
        #ifdef VERBOSE
        std::cout << "PROBLEMON: el tipo del nodo es cualquier cosa!!" << std::endl;
        #endif
    }
}
void romper_todo(nodeType *n)
{
    if(n!=NULL)
    {
        nodeType * hijo1 = (nodeType *) n->hijo1;
        nodeType * hijo2 = (nodeType *) n->hijo2;
        nodeType * hijo3 = (nodeType *) n->hijo3;
        romper_todo(hijo1);
        romper_todo(hijo2);
        romper_todo(hijo3);
        delete n;
    }

}
void analizo_grafo(nodeType * n) 
{
    if(n->tipo != RAIZ)
    {
        #ifdef VERBOSE
        std::cout << "PROBLEMON: la raiz no es raiz!!!" << std::endl;
        #endif
    }
    mi_output.open("bla.ps");
    n->escala=1.0f;
    propagar_escala(n);
    mi_output << "%!PS-Adobe-3.0 EPSF-3.0" << std::endl;
    mi_output << "%%BoundingBox: 0 0 " << (int(n->ancho)+ 1) *12 +3 << " "<< (int(n->h1 + n->h2)+1) *12 + 9<< std::endl;
    mi_output << "2 " << int(12.0f*n->h2) + 4<< " translate" << std::endl;
    mi_output << "12 12 scale" << std::endl;
    mi_output << ".03 setlinewidth" << std::endl;
    mi_output << "/Courier findfont setfont" << std::endl;
    n->x = 0;
    n->y = 0;
    propagar_heredados(n);
    #ifdef VERBOSE
    std::cout << "la rompo en yacc" << std::endl;
    #endif
    romper_todo(n);
    mi_output.close();
}   


// (A^BC^D/E^F_G+H)-I
